/*!
    \copyright  Copyright (c) 2024 Qualcomm Technologies International, Ltd.\n
                All Rights Reserved.\n
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    handset_service
    \brief      Handset service acceptor list
*/

#include "handset_service_acceptor_list.h"

#include "connection_manager.h"

#include <logging.h>
#include <panic.h>


/*! \brief Maximum number of entries the list can hold. */
#define HANDSET_SERVICE_ACCEPTOR_LIST_MAXIMUM_LIST_ENTRIES  2

/*! \brief The default maximum number of entries that the module is initialised to. */
#define HANDSET_SERVICE_ACCEPTOR_LIST_MAXIMUM_LIST_ENTRIES_DEFAULT  1


typedef struct
{
    unsigned has_bredr_link:1;
    unsigned has_le_link:1;
    typed_bdaddr ota_taddr;
    typed_bdaddr perm_taddr;
} handset_service_acceptor_list_entry_t;

typedef struct
{
    uint8 max_entry_count;
    uint8 entry_count;

    handset_service_acceptor_list_entry_t list[HANDSET_SERVICE_ACCEPTOR_LIST_MAXIMUM_LIST_ENTRIES];
} handset_service_acceptor_list_t;


handset_service_acceptor_list_t handset_service_acceptor_list = {0};

#define handsetService_AcceptorListGet() (&handset_service_acceptor_list)


void handsetService_AcceptorListInit(void)
{
    memset(&handset_service_acceptor_list, 0, sizeof handset_service_acceptor_list);
    handsetService_AcceptorListRemoveAllHandsets();
    handsetService_AcceptorListSetMaximumHandsetCount(HANDSET_SERVICE_ACCEPTOR_LIST_MAXIMUM_LIST_ENTRIES_DEFAULT);
}

bool handsetService_AcceptorListSetMaximumHandsetCount(unsigned max_handset_count)
{
    handset_service_acceptor_list_t *list = handsetService_AcceptorListGet();

    PanicFalse(max_handset_count <= HANDSET_SERVICE_ACCEPTOR_LIST_MAXIMUM_LIST_ENTRIES);

    list->max_entry_count = max_handset_count;

    return TRUE;
}

unsigned handsetService_AcceptorListGetHandsetCount(void)
{
    handset_service_acceptor_list_t *list = handsetService_AcceptorListGet();
    return list->entry_count;
}

static handset_service_acceptor_list_entry_t *handsetService_AcceptorListFindEntryByTpBdAddr(const tp_bdaddr *tpaddr)
{
    handset_service_acceptor_list_t *list = handsetService_AcceptorListGet();
    handset_service_acceptor_list_entry_t *entry = NULL;

    ARRAY_FOREACH(entry, list->list)
    {
        if (BdaddrTypedIsSame(&tpaddr->taddr, &entry->ota_taddr) || BdaddrTypedIsSame(&tpaddr->taddr, &entry->perm_taddr))
        {
            return entry;
        }
    }

    return NULL;
}

static handset_service_acceptor_list_entry_t *handsetService_AcceptorListCreateEntry(const tp_bdaddr *tpaddr)
{
    handset_service_acceptor_list_t *list = handsetService_AcceptorListGet();

    if (list->entry_count >= list->max_entry_count)
    {
        return NULL;
    }

    for (int i = 0; i < list->max_entry_count; i++)
    {
        handset_service_acceptor_list_entry_t *entry = &list->list[i];

        if (BdaddrTypedIsEmpty(&entry->ota_taddr))
        {
            tp_bdaddr resolved_addr = {0};

            if (ConManagerResolveTpaddr(tpaddr, &resolved_addr))
            {
                ConManagerSendAddDeviceToLeWhiteListRequest(&resolved_addr.taddr);
            }

            entry->ota_taddr = tpaddr->taddr;
            entry->perm_taddr = resolved_addr.taddr;
            list->entry_count++;

            return entry;
        }
    }

    return NULL;
}

static void handsetService_AcceptorListRemoveEntry(handset_service_acceptor_list_entry_t *entry)
{
    ConManagerSendRemoveDeviceFromLeWhiteListRequest(&entry->perm_taddr);

    BdaddrTypedSetEmpty(&entry->ota_taddr);
    BdaddrTypedSetEmpty(&entry->perm_taddr);

    entry->has_bredr_link = FALSE;
    entry->has_le_link = FALSE;
}

bool handsetService_AcceptorListCanAddHandset(const tp_bdaddr *tpaddr)
{
    bool can_add = FALSE;

    if (handset_service_acceptor_list.entry_count < handset_service_acceptor_list.max_entry_count)
    {
        can_add = TRUE;
    }
    else
    {
        handset_service_acceptor_list_entry_t *entry = handsetService_AcceptorListFindEntryByTpBdAddr(tpaddr);
        if (entry)
        {
            can_add = TRUE;
        }
    }

    DEBUG_LOG("handsetService_AcceptorListCanAddHandset: %2x %04x %02x %06lx can_add=%u",
              tpaddr->taddr.type, tpaddr->taddr.addr.nap, tpaddr->taddr.addr.uap, tpaddr->taddr.addr.lap, can_add);

    return can_add;
}

bool handsetService_AcceptorListAddHandset(const tp_bdaddr *tpaddr)
{
    bool added = FALSE;
    tp_bdaddr resolved_addr = {0};
    handset_service_acceptor_list_entry_t *entry = NULL;

    if (!ConManagerResolveTpaddr(tpaddr, &resolved_addr))
    {
        DEBUG_LOG_WARN("handsetService_AcceptorListAddHandset WARNING: Couldn't resolve address");
    }

    entry = handsetService_AcceptorListFindEntryByTpBdAddr(&resolved_addr);
    if (entry)
    {
        if (tpaddr->taddr.type == TYPED_BDADDR_RANDOM)
        {
            entry->ota_taddr = tpaddr->taddr;
        }
    }
    else
    {
        entry = handsetService_AcceptorListCreateEntry(tpaddr);
    }

    if (entry)
    {
        if (tpaddr->transport == TRANSPORT_BREDR_ACL)
        {
            entry->has_bredr_link = TRUE;
        }
        else if (tpaddr->transport == TRANSPORT_BLE_ACL)
        {
            entry->has_le_link = TRUE;
        }
        else
        {
            DEBUG_LOG_PANIC("handsetService_AcceptorListAddHandset: bad transport 0x%02X", tpaddr->transport);
        }

        added = TRUE;
    }

    DEBUG_LOG("handsetService_AcceptorListAddHandset: %2x %04x %02x %06lx added=%u",
              tpaddr->taddr.type, tpaddr->taddr.addr.nap, tpaddr->taddr.addr.uap, tpaddr->taddr.addr.lap, added);

    return added;
}

void handsetService_AcceptorListRemoveHandset(const tp_bdaddr *tpaddr)
{
    handset_service_acceptor_list_t *list = handsetService_AcceptorListGet();
    handset_service_acceptor_list_entry_t *entry = NULL;

    DEBUG_LOG_FN_ENTRY("handsetService_AcceptorListRemoveHandset: 0x%01x %04x,%02x,%06lx",
                       tpaddr->taddr.type,
                       tpaddr->taddr.addr.nap,
                       tpaddr->taddr.addr.uap,
                       tpaddr->taddr.addr.lap);

    entry = handsetService_AcceptorListFindEntryByTpBdAddr(tpaddr);

    if (entry)
    {
        if (tpaddr->transport == TRANSPORT_BREDR_ACL)
        {
            entry->has_bredr_link = FALSE;
        }
        else if (tpaddr->transport == TRANSPORT_BLE_ACL)
        {
            entry->has_le_link = FALSE;
        }
        else
        {
            DEBUG_LOG_PANIC("handsetService_AcceptorListRemoveHandset: bad transport 0x%02X", tpaddr->transport);
        }

        if (!entry->has_bredr_link && !entry->has_le_link)
        {
            handsetService_AcceptorListRemoveEntry(entry);
            list->entry_count--;
        }
    }
}

void handsetService_AcceptorListRemoveAllHandsets(void)
{
    handset_service_acceptor_list_entry_t *entry = NULL;

    DEBUG_LOG_FN_ENTRY("handsetService_AcceptorListRemoveAllHandsets");

    handset_service_acceptor_list.entry_count = 0;

    ARRAY_FOREACH(entry, handset_service_acceptor_list.list)
    {
        handsetService_AcceptorListRemoveEntry(entry);
    }
}

void handsetService_AcceptorListSanitise(void)
{
    handset_service_acceptor_list_entry_t *entry = NULL;

    ARRAY_FOREACH(entry, handset_service_acceptor_list.list)
    {
        bool is_random = entry->perm_taddr.type == TYPED_BDADDR_RANDOM;

        if (is_random)
        {
            tp_bdaddr unresolved_addr = {
                .transport = TRANSPORT_BLE_ACL,
                .taddr = entry->perm_taddr
            };
            tp_bdaddr resolved_addr = {0};

            bool is_resolved = ConManagerResolveTpaddr(&unresolved_addr, &resolved_addr);

            if (is_resolved)
            {
                handset_service_acceptor_list_entry_t *existing_entry = NULL;

                handsetService_AcceptorListRemoveHandset(&unresolved_addr);

                existing_entry = handsetService_AcceptorListFindEntryByTpBdAddr(&resolved_addr);
                if (existing_entry)
                {
                    existing_entry->has_le_link = TRUE;
                    existing_entry->ota_taddr = unresolved_addr.taddr;
                    ConManagerSendAddDeviceToLeWhiteListRequest(&resolved_addr.taddr);
                }
                else
                {
                    handsetService_AcceptorListAddHandset(&unresolved_addr);
                }

            }
        }
    }
}
